When writing code, it’s common to take common pieces
of code and reuse then in a number of common ways including creating
base classes, creating static classes or reusable libraries. In XAML,
the same need for creating reusability. This reusability though is more
about creating a consistent look of the application without having to
copy the same code over and over. Consider this common XAML:
<TextBox x:Name="nameBox"
FontSize="36"
FontFamily="Segoe WP"
FontWeight="Black"
BorderBrush="Blue"
Foreground="White"
HorizontalAlignment="Stretch" />
<TextBox x:Name="emailBox"
FontSize="36"
FontFamily="Segoe WP"
FontWeight="Black"
BorderBrush="Blue"
Foreground="White"
HorizontalAlignment="Stretch" />
In this XAML many of the
properties are copied from one of the TextBoxes to the other. If we
change any of the properties of one, we will have to copy the change to
the other to provide consistency of the UI. In addition, the Foreground and BorderBrush
are using colors that could or should be part of an overall look and
feel. It’s likely that we would want the brushes used there to be
consistent not only TextBox to TextBox but across the entire application. That’s where Styling and Resources come in.
Understanding Resources
The first level of
consistency has to do with sharing common resources. When creating an
application, you often will want to use common colors or brushes across
an application. Silverlight allows you to create objects to be used in
more than one area by specifying them in a Resource section and
identifying it with an x:Key attribute. For example, you could define a
SolidColorBrush in a resource section like so:
<Grid x:Name="LayoutRoot">
<Grid.Resources>
<SolidColorBrush x:Key="mainBrush"
Color="Blue" />
</Grid.Resources>
...
</Grid>
Every class that derives from FrameworkElement (which means most XAML elements) supports a collection of Resources. These named elements can be referred to using the StaticResource markup extension like so:
<Grid x:Name="LayoutRoot">
<Grid.Resources>
<SolidColorBrush x:Key="mainBrush"
Color="Blue" />
</Grid.Resources>
<TextBlock Foreground="{StaticResource mainBrush}"
Text="Hello World" />
...
</Grid>
The StaticResource
markup extension tells the XAML parser to replace the Foreground with
the main brush. You can use the resource in several places which
isolates it from changes. So that later when you change main brush to a
LinearGradientBrush, it cascades down to wherever the StaticResource was used.
The StaticResource
markup extension looks up through the XAML document to find the
resource with the correct name (through the hierarchy), it will
continue beyond the beginning of the XAML document. Above the XAML
document is the App.xaml file in the
phone application project. Normally this is where you would place any
application-wide resources. For example, if the App.xaml file looked like this:
<Application x:Class="PhoneControls.App"
xmlns="..."
xmlns:x="..."
xmlns:phone="..."
xmlns:shell="...">
<!--Application Resources-->
<Application.Resources>
<SolidColorBrush x:Key="mainBrush"
Color="Blue" />
</Application.Resources>
...
</Application>
The mainBrush is then defined at the application level so any XAML document that wanted to use the brush could do so like so:
<Grid x:Name="LayoutRoot">
<TextBlock Foreground="{StaticResource mainBrush}"
Text="Hello World" />
...
</Grid>
While this example shows a
brush (which is a very commonly shared resource), it is not limited to
only brushes. Any creatable object can be used in this way. In
addition, when you want to share these resources across projects you
can accomplish this with ResourceDictionary objects. Resource dictionaries are XAML files that contain shared resources that can be imported into App.xaml
using merged dictionaries. These dictionaries can be flat XAML files or
contained in separate assemblies that are referenced to your phone
application. For more information on merged dictionaries, see the
documentation.
Styles
While sharing resources can
help define a common look and feel, the styling stack extends this idea
by allowing you to specify the default properties for controls in a
common place. The Style object allows you to create these default
properties:
<Style TargetType="TextBox"
x:Key="mainTextBox">
<Setter Property="FontSize"
Value="18" />
<Setter Property="FontFamily"
Value="Segoe WP Bold" />
</Style>
The Style object takes the
type of object it can be applied to then a set of Setter objects that
define default values for properties. In this example, the FontSize and
FontFamily for a TextBox are supplied. To apply this style to an
object, you must map it to the Style property on an element via the StaticResource markup extension like so:
<TextBox Style="{StaticResource mainTextBox}" />
By setting this TextBox’s Style property using the StaticResource markup extension, the default property values of the TextBox will be set using the Style. Styles are just named resources so you would typically place them in the App.xaml file along with other resources. In addition, you can use resources inside your styles as well like so:
<Application.Resources>
<SolidColorBrush x:Key="mainBrush"
Color="Blue" />
<Style TargetType="TextBox"
x:Key="mainTextBox">
<Setter Property="FontSize"
Value="18" />
<Setter Property="FontFamily"
Value="Segoe WP Bold" />
<Setter Property="Foreground"
Value="{StaticResource mainBrush}" />
</Style>
</Application.Resources>
In this way the shared
resources can cascade down into the styling stack. In addition, the
Styles themselves can be cascaded by using the BasedOn property:
<Application.Resources>
<SolidColorBrush x:Key="mainBrush"
Color="Blue" />
<Style TargetType="TextBox"
x:Name="baseTextBox">
<Setter Property="FontSize"
Value="18" />
<Setter Property="FontFamily"
Value="Segoe WP Bold" />
</Style>
<Style TargetType="TextBox"
x:Key="mainTextBox"
BasedOn="{StaticResource baseTextBox}">
<Setter Property="Foreground"
Value="{StaticResource mainBrush}" />
</Style>
</Application.Resources>
Finally, styles can be polymorphic. In other words, the TargetType may apply to a base class and be applied to all objects of that type. For example:
<Application.Resources>
<SolidColorBrush x:Key="mainBrush"
Color="Blue" />
<Style TargetType="Control"
x:Name="baseControl">
<Setter Property="BorderBrush"
Value="Black" />
</Style>
<Style TargetType="TextBox"
x:Key="mainTextBox"
BasedOn="{StaticResource baseControl}">
<Setter Property="FontSize"
Value="18" />
<Setter Property="FontFamily"
Value="Segoe WP Bold" />
<Setter Property="Foreground"
Value="{StaticResource mainBrush}" />
</Style>
</Application.Resources>
Since the TargetType of the base style was Control, it could be used as the BasedOn for any controls (or even the Style for any control that derived from the Control class).
While property-based
styling is very powerful, it may not let you change the look of the
controls in dramatic ways. That is where Control Templates come in.